// 写一个http服务器类Httpx
// 监听用的sock
// init(int port)监听需要bind和listen的几个步骤合并成一个成员函数
// Httpx(int port){init(port);}
// ~Httpx需要析构的时候关掉
// 每次accept，都得到一个新client，需要创建线程，线程执行“业务函数”
// vector<thread>或者map
// thread_del，用来释放线程资源
// 业务函数：解析http请求报文，得到GET/POST和url，并把内容发过去
// 得到报文
// 解析出GET/POST
// 解析出url，从而得到路径
// 错误处理，如404等

#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>

#include <cstring>
#include <iostream>
#include <string>

#include "login.hpp"
#include "url.hpp"

using std::cout;
using std::endl;
using std::string;

const string PATH = "html";
const string HTML = "/index.html";

void* accept_request(void* arg);
string get_line(int sock);
int send_file(int client_fd, const char* file_path, string file_tp);

class Httpx {
 public:
  Httpx(int _port = 5005) {
    server_sock = init(_port);
    if (server_sock == -1) cout << "init failed!" << endl;
    cout << "Http xserver is running on port:" << _port << endl;
  }

  ~Httpx() {
    if (server_sock > 0) close(server_sock);
  }

  void Accept();

 private:
  int server_sock = -1;  // 监听用的sock

  int init(int port);  // 初始化服务器并开始监听
};

int Httpx::init(int port) {
  int httpfd = -1;
  // 获得socket
  httpfd = socket(AF_INET, SOCK_STREAM, 0);
  if (httpfd == -1) {
    cout << "socket wrong!" << endl;
    return -1;
  }

  // 设置地址
  sockaddr_in name;
  memset(&name, 0, sizeof(name));
  name.sin_family = AF_INET;
  name.sin_port = htons(port);
  name.sin_addr.s_addr = htonl(INADDR_ANY);

  // 设置SO_REUSEADDR
  int opt = 1;
  setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
  // setsockopt(httpfd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(int));

  // bind
  if (bind(httpfd, (sockaddr*)&name, sizeof(name)) < 0) {
    cout << "bind wrong!" << endl;
    close(httpfd);
    return -1;
  }
  if (listen(httpfd, 5) != 0) {
    cout << "listen wrong!" << endl;
    close(httpfd);
    return -1;
  }
  return httpfd;
}

// void Httpx::Accept() {
//   int epollfd = epoll_create(10);
//   // 添加监听描述符事件
//   epoll_event evt;
//   evt.data.fd = server_sock;
//   evt.events = EPOLLIN;
//   epoll_ctl(epollfd, EPOLL_CTL_ADD, server_sock, &evt);
//   // 进入循环
//   while (1) {
//     // epoll过程
//     epoll_event evtset[1000];  // 存放有事件发生的结构体数组
//     int infds =
//         epoll_wait(epollfd, evtset, 1000, -1);  // 等待监视的socket有时间发生
//     for (int i = 0; i < infds; ++i) {          // 遍历有事件发生的
//       // 监听
//       if (evtset[i].data.fd == server_sock) {
//       //if ((evtset[i].data.fd == server_sock) && (evtset[i].events &
//       EPOLLIN)) {
//         // 新客户
//         int client_sock = -1;
//         sockaddr_in client_name;
//         socklen_t len = sizeof(client_name);
//         client_sock = accept(server_sock, (sockaddr*)&client_name, &len);
//         // 添加到epoll
//         memset(&evt, 0, sizeof(epoll_event));
//         evt.data.fd = client_sock;
//         evt.events = EPOLLIN;
//         epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sock, &evt);
//       }
//       // 断开链接或客户数据来了
//       else{
//       //else if (evtset[i].events & EPOLLIN) {
//         accept_request(evtset[i].data.fd);
//         memset(&evt, 0, sizeof(epoll_event));
//         evt.events = EPOLLIN;
//         evt.data.fd = evtset[i].data.fd;
//         epoll_ctl(epollfd, EPOLL_CTL_DEL, evtset[i].data.fd, &evt);
//       }
//     }
//   }
//   close(epollfd);
// }

void Httpx::Accept() {
  int client_sock = -1;
  struct sockaddr_in client_name;
  socklen_t client_name_len = sizeof(client_name);
  pthread_t newthread;

  while (1) {
    client_sock =
        accept(server_sock, (struct sockaddr*)&client_name, &client_name_len);
    pthread_create(&newthread, NULL, accept_request,
                   (void*)(intptr_t)client_sock);
  }
}

void* accept_request(void* arg) {
  // 获得报文头、请求、url
  int client_sock = (intptr_t)arg;
  string buf = get_line(client_sock);
  string method = get_method(buf);
  string url = get_url(buf);

  // 处理url
  if (url == "/") url = HTML;
  int pos = url.find('?'); // 处理有参数的消息
  if (pos != url.npos) update_ump(url, pos);
  string file_tp = get_file_type(url);
  if (file_tp == "null") {
    cout << "wrong url" << endl;
    close(client_sock);
    pthread_exit(0);
  }

  // 文件路径
  url = PATH + url;
  const char* full_path = url.data();

  // 发送文件给client
  if (send_file(client_sock, full_path, file_tp) < 0) {
    cout << "send_file wrong!" << endl;
    close(client_sock);
    pthread_exit(0);
  }
  sleep(1);
  close(client_sock);
}

string get_line(int sock) {
  string buf;
  char c = 0;
  while (c != '\n') {
    int n = recv(sock, &c, 1, 0);
    if (n > 0) {
      if (c == '\r') {
        n = recv(sock, &c, 1, MSG_PEEK);
        if ((n > 0) && (c == '\n'))
          recv(sock, &c, 1, 0);
        else
          c = '\n';
      }
      buf += c;
    } else
      c = '\n';
  }
  return buf;
}

int send_file(int client_fd, const char* file_path, string file_tp) {
  // 打开文件
  int fd = open(file_path, 2);
  if (fd < 0) return -1;

  // 获取文件大小
  struct stat s_buff;
  fstat(fd, &s_buff);

  // 构建头部
  char buf[1024];
  sprintf(buf,
          "HTTP/1.1 200 OK\r\n"
          "Content-type:%s\r\n"
          "Content-Length:%d\r\n"
          "\r\n",
          file_tp.data(), s_buff.st_size);
  if (send(client_fd, buf, strlen(buf), 0) != strlen(buf))
    return -2;  // 发送头部

  // 发送内容
  int cnt = 0;
  while (1) {
    cnt = read(fd, buf, 1024);
    if (send(client_fd, buf, cnt, 0) != cnt) return -3;
    if (cnt != 1024) break;
  }
  return 0;
}